package br.unicamp.mc715;

import java.io.Serializable;
import java.net.UnknownHostException;

import junit.framework.TestCase;
import br.unicamp.mc715.common.MC715IOException;
import br.unicamp.mc715.common.MC715SerializationException;
import br.unicamp.mc715.common.RandomString;
import br.unicamp.mc715.transport.Message;
import br.unicamp.mc715.transport.Transport;
import br.unicamp.mc715.transport.UDPMessage;
import br.unicamp.mc715.transport.UDPTransport;

public class UDPTransportTest extends TestCase {
    Transport transport;
    public static Serializable payload1;
    public static Serializable payload2;
    public static Serializable payload3;
    public static Serializable payload4;
    public static final int TIMEOUT = 500;
    
    @Override
    protected void setUp() throws Exception {
        transport = new UDPTransport();
        System.out.println("Transport ID: " + transport.getId());
        payload1 = RandomString.RANDOM_256.nextString();
        payload2 = RandomString.RANDOM_256.nextString();
        payload3 = RandomString.RANDOM_256.nextString();
        payload4 = RandomString.RANDOM_256.nextString();
    }
    
    /** Testa a unicidade dos IDs gerados */
    public void testMyId() throws MC715IOException, UnknownHostException {
        Transport[] other = new UDPTransport[100];
        
        for(int i = 0; i < 100 ; i++)
        	other[i] = new UDPTransport();
        
        for(int i = 0; i < 100; i++)
        	for(int j = 99; j > i; j--)
        		assertFalse("IDs iguais", other[i].getId().equals(other[j].getId()));
    }

    /** Testa o envio de uma mensagem para mim mesmo */
    public void testEcho() throws MC715IOException, MC715SerializationException {
        transport.sendMessage(payload1, transport.getId());

        Message recv = transport.receiveMessage(TIMEOUT);
        assertNotNull("Dado não foi recebido de volta",recv);
        assertEquals("Dado recebido não é igual ao dado enviado" , recv.getPayload(), payload1);
        
        // Mais nada a receber
        assertNull("Não deveria receber nada", transport.receiveMessage(TIMEOUT));
    }

    /** Testa o envio de uma mensagem a outro transporte */
    public void testSend() throws MC715IOException, MC715SerializationException {
        Transport receiver = new UDPTransport();
        assertFalse(receiver.getId().equals(transport.getId()));

        transport.sendMessage(payload1, receiver.getId());

        // A mensagem deve ser recebida pelo destinatÃ¡rio
        Message recv = receiver.receiveMessage(TIMEOUT);
        assertNotNull("Dado não foi recebido de volta",recv);
        assertEquals("Dado recebido não é igual ao dado enviado" , recv.getPayload(), payload1);
        assertEquals("Remetente real e remetente informado diferem", recv.getSender(), transport.getId());
        assertEquals("Destinatário real e destinatário informado diferem", recv.getReceiver(), receiver.getId());
        //Mais nada a receber
        assertNull("Não deveria receber nada", receiver.receiveMessage(TIMEOUT));
        
        //A mensagem Não deve ser recebida pelo remetente
        assertNull("Não deveria receber nada", transport.receiveMessage(TIMEOUT));
    }
    
    /** Testa o envio de mensagens muito grandes e aleatorias */
    public void testMultPayloads() throws MC715IOException, MC715SerializationException {
    	
    	// Cria objetos basicos
    	Transport receiver = new UDPTransport();
        Message[] recv = new UDPMessage[250];
        assertFalse(receiver.getId().equals(transport.getId()));
        
    	RandomString stringGen = new RandomString(500);
    	Serializable bigPayload[] = new String[250];
    	
    	// Instancia os payloads
    	for (int i=0; i < 250; i++) {
    		bigPayload[i] = i + ":" + stringGen.nextString();
    	}
    	
    	// Envia as mensagens primeiro por unicast e depois por multicast
    	for (int i = 0; i < 250; i++)
    		transport.sendMessage(bigPayload[i], receiver.getId());
    	
    	// Recebe todas as mensagens para receiver
    	for (int i = 0; i < 250; i++) {
    		recv[i] = receiver.receiveMessage(TIMEOUT);
    		assertNotNull("Dado nao foi recebido", recv[i]);
    	}
    	
       for(int i = 0; i < 250; i++)
        {
        	String msg = (String)recv[i].getPayload();
        	int num = Integer.parseInt(msg.toString().substring(0, msg.toString().indexOf(":")));
        	assertEquals("Dado recebido não é igual ao dado enviado" , recv[i].getPayload(), bigPayload[num]);
        }
        
        // Mais nada a receber
        assertNull("Não deveria receber nada", transport.receiveMessage(TIMEOUT));
        assertNull("Não deveria receber nada", receiver.receiveMessage(TIMEOUT));
    }
    
    /** Testa a fila de mensagens */
    public void testQueue() throws MC715IOException, MC715SerializationException {
        transport.sendMessage(payload1, transport.getId());
        transport.sendMessage(payload2, transport.getId());
        transport.sendMessage(payload3, transport.getId());
        assertEquals(payload1, transport.receiveMessage(TIMEOUT).getPayload());
        assertEquals(payload2, transport.receiveMessage(TIMEOUT).getPayload());
        transport.sendMessage(payload4, transport.getId());
        assertEquals(payload3, transport.receiveMessage(TIMEOUT).getPayload());
        assertEquals(payload4, transport.receiveMessage(TIMEOUT).getPayload());
        
        // Mais nada a receber
        assertNull("Não deveria receber nada", transport.receiveMessage(TIMEOUT));
    }
    
    /** Testa o envio de uma mensagem em multicast */
    public void testMulticast() throws MC715IOException, MC715SerializationException {
        Transport receiver = new UDPTransport();
        assertFalse(receiver.getId().equals(transport.getId()));
        transport.sendMessage(payload1);

        // A mensagem deve ser recebida pelos 2 sockets
        Message recv = transport.receiveMessage(TIMEOUT);
        assertNotNull("Dado não foi recebido de volta",recv);
        assertEquals("Dado recebido não é igual ao dado enviado" , recv.getPayload(), payload1);
        assertEquals("Remetente real e remetente informado diferem", recv.getSender(), transport.getId());
        assertEquals("Destinatário real e destinatário informado diferem", recv.getReceiver(), transport.getId());
        
        // A mensagem deve ser recebida pelos 2 sockets
        recv = receiver.receiveMessage(TIMEOUT);
        assertNotNull("Dado não foi recebido de volta",recv);
        assertEquals("Dado recebido não é igual ao dado enviado" , recv.getPayload(), payload1);
        assertEquals("Remetente real e remetente informado diferem", recv.getSender(), transport.getId());
        assertEquals("Destinatário real e destinatário informado diferem", recv.getReceiver(), receiver.getId());
        
        // Mais nada a receber
        assertNull("Não deveria receber nada", transport.receiveMessage(TIMEOUT));
        assertNull("Não deveria receber nada", receiver.receiveMessage(TIMEOUT));
    }
    
    /** Testa o envio de mensagens muito grandes e aleatorias em multcast e unicast */
    public void testMulticastBigPayloads() throws MC715IOException, MC715SerializationException {
    	
    	// Cria objetos basicos
    	Transport receiver = new UDPTransport();
        Message[] recv = new UDPMessage[200];
        assertFalse(receiver.getId().equals(transport.getId()));
        
    	RandomString stringGen = new RandomString(250);
    	Serializable bigPayload[] = new String[100];
    	
    	// Instancia os payloads
    	for (int i=0; i < 100; i++) {
    		bigPayload[i] = i+":"+stringGen.nextString();
    	}
    	
    	// Envia as mensagens primeiro por unicast e depois por multicast
    	for (int i = 0; i < 100; i++) {
    		transport.sendMessage(bigPayload[i], receiver.getId());
    		transport.sendMessage(bigPayload[i]);
    	}
    	
    	// Recebe todas as mensagens para receiver, unicast e multicast
    	for (int i = 0; i < 200; i++) {
    		recv[i] = receiver.receiveMessage(TIMEOUT);
    		assertNotNull("Dado nao foi recebido",recv[i]);
    	}
    	
    	// Recebe as mensagens multcast para transport
    	for (int i=0; i < 100; i++) {
    		assertNotNull("Dado nao foi recebido",transport.receiveMessage(TIMEOUT));
    	}
    	
        for(int i = 0; i < 200; i++)
        {
        	String msg = (String)recv[i].getPayload();
        	int num = Integer.parseInt(msg.toString().substring(0, msg.toString().indexOf(":")));
        	assertEquals("Dado recebido não é igual ao dado enviado" , recv[i].getPayload(), bigPayload[num]);
        }
        
        // Mais nada a receber
        assertNull("Não deveria receber nada", transport.receiveMessage(TIMEOUT));
        assertNull("Não deveria receber nada", receiver.receiveMessage(TIMEOUT));
    }
}
